package node;
import log.Logger;
import messaging.InterNodeMessage;
import messaging.MessageChannels;
import messaging.MessageHashing;
import messaging.NodeRemoveNodeListPack;
import messaging.RemoveNodeChannel;
import messaging.RemoveNodeMessage;

import java.io.Serializable;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.NodeList;

import remote.RemoteDistributedHashMap;
import remote.UnicastRemoteDistributedHashMap;

/**
 * Removing the node.
 * @author Team4
 * @version 19th May 2013
 *
 */
public class NodeRemoval
{
  /**
   * Stores the node no to be deleted.
   */
  private int my_node_num;
  /**
   * Stores the total no of nodes to be deleted.
   */
  private int my_total_no_nodes;
  /**
   * Stores the real and virtual node info in the registry.
   */
  private Map<Integer, Boolean> my_real_virtual_node_info;
  /**
   * Stores the Registry.
   */
  private Registry my_registry; 
  /**
   * Stores an instance of NodeManager
   */
  private NodeManager my_node_manager;
  /**
   * Instantiates the node information and the circle information.
   * @param the_node_num : Stores the node no to be deleted.
   * @param the_total_no_nodes : Stores the total no of nodes to be deleted.
   * @param the_remote_registry_name : Stores the remote registry name.
   * @param the_host_name : Stores the host name. 
   * @param the_port_no : Stores the port number.
   * @param the_real_virtual_node_info : Stores the real and virtual node 
   * info in the registry.
   * @throws RemoteException : remote exception if the object is not found 
   * @throws NotBoundException : if the given registry name is not found
   */
  public NodeRemoval(final int the_node_num, 
                     final int the_total_no_nodes,
                     final Registry the_registry,
                     final Map<Integer, Boolean> the_real_virtual_node_info, 
                     final NodeManager the_node_manager) 
                         throws RemoteException, NotBoundException
  {
    
    my_node_num = the_node_num;
    my_total_no_nodes = the_total_no_nodes;
    my_real_virtual_node_info = the_real_virtual_node_info;
    my_registry = the_registry;
    my_node_manager = the_node_manager;
    }
  
  /**
   * Removes the Node from the registry.
   * @return : true if the node removal is successful
   * @throws NotBoundException 
   * @throws RemoteException 
   * @throws AccessException 
   * @throws InterruptedException 
   */
  public boolean removeNode() throws AccessException, RemoteException, NotBoundException, InterruptedException
  {
    
    boolean is_node_removed = true;
    //**********************************Remove Code
    for(int index=0; index<my_real_virtual_node_info.size();index++)
    {
      System.out.println("Node No: "+ my_real_virtual_node_info.get(index));
    }
    if(my_node_manager.node_connections.isEmpty())
    {
      System.out.println("Hey I'm empty: node connections");
    }
    
    //******************************
    if (my_node_num >= my_total_no_nodes)
    {
      Logger.err("The number of actual node need to " +
          "remove is greater tha  n the total number in the ring.");
      System.exit(1);
      is_node_removed = false;
    }
    //Is this real_virtual only to one node Manager.. if yes
    //use the string[] of namespaces to get this
    /*else if (!my_real_virtual_node_info.get(my_node_num))
    {
      Logger.err("This node: Node" + my_node_num + "is not an actual node");
      final String tempfilename = "LOG-" + my_node_num + ".txt";
      Logger.err("Node" + my_node_num + " is not an actual node");
      Logger.writeContent(tempfilename, "Wrong request of the removing actual node, " +
          "this node is not actual.");
      is_node_removed = false;
    }*/
    
    ////Above code written on client..if activated real virtual is empty
    else 
    {
      my_node_manager.messageChannelSetup(0);
      my_node_manager.QueueThreadStart(0);
      final int simulating_node = findPredecessor();
      if (simulating_node == -1 || simulating_node == my_node_num)
      {
        //if only one actual node is present
        is_node_removed = false;
        final String tempfilename = "LOG-" + my_node_num + ".txt";
        Logger.err("Node" + my_node_num + " is the only actual node");
        Logger.writeContent(tempfilename, "Only one actual node present");
      }
      else 
      {
        //ArrayList<Integer> node_list = my_node_manager.node_set;
        //ArrayList<Integer> node_list = new ArrayList<Integer>();
        /*for(int i=0; i< my_node_manager.node_set.size(); i++)
        {
          node_list.add(my_node_manager.node_set.get(i));
        }*/
       NodeRemoveNodeListPack node_list_pack = new NodeRemoveNodeListPack(my_node_manager);
       ArrayList<Integer> node_list = node_list_pack.nodeList();
        ArrayList<RemoveNodeChannel> node_message_channel_list = new ArrayList<RemoveNodeChannel>();
        ArrayList<HashMap<String, Serializable>> node_data = new ArrayList<HashMap<String, Serializable>>();
        for(int index = 0; index < node_list.size(); index++)
        {
          System.out.println("Hi I'm in loop");
          RemoveNodeChannel remove_node_channel = new RemoveNodeChannel(node_list.get(index), my_node_manager);
          HashMap<String, Serializable> node_data_one = (HashMap<String, Serializable>) 
              my_node_manager.node_stubs.get(node_list.get(index)).getData();
          node_message_channel_list.add(remove_node_channel);
          NodeRemoveDataPack node_data_pack = new NodeRemoveDataPack(my_node_manager, node_list.get(index));
          node_data.add(node_data_pack.nodeData());
          
        }
        int the_predecessor = findPredecessor();
        RemoveNodeMessage message = new RemoveNodeMessage(my_node_num,node_list,node_data);
        
        MessageHashing message_hashing =
            new MessageHashing(my_node_manager.node_connections); 
        final int dest_node = message_hashing.optimalNodeFind(my_node_num, the_predecessor);
        
        final InterNodeMessage inter_message = new InterNodeMessage(my_node_num, 
                                                               the_predecessor, 
                                                               my_node_num, 
                                                               dest_node, 5);
        inter_message.setAnswer_set(false);
        inter_message.setRemoveNodeMessage(message);
        final String dest_node_str = my_node_manager.rmi_NodeFind(dest_node);
        final RemoteDistributedHashMap to_send =
          (RemoteDistributedHashMap) NodeManager.registry.lookup(dest_node_str);
        Logger.writeContent(my_node_manager.LOGGER_FILE_NAME, 
                            " sending a message to predecessor node " +
                            the_predecessor + " for removing node");
        Logger.out(" sending a message to predecessor node " + 
            the_predecessor + " for adding node");
        Logger.out(" message routed through node " + dest_node);
        /*Logger.writeContent(my_node_manager.LOGGER_FILE_NAME,
                            " message routed through node " + dest_node);*/
        System.out.println("Node Remove:NodeRemoval : Sending message");
        
        to_send.send(inter_message);
        
        //Does the node communication and removal   
      }
    }
    System.out.println("Node Remove:NodeRemoval : removeNode()");
    
    return is_node_removed;
  }
  
  /**
   * Find the NodeManager of the predecessor Active node.
   * @return the active node manager
   * @throws RemoteException remote exception
   * @throws AccessException access exception
   * @throws NotBoundException not bound exception
   */
  private int findPredecessor() throws AccessException, RemoteException, NotBoundException
  {
    int index = my_node_num - 1;
    if (index == NodeManager.total_nodes_chord)
    {
      index = 0;
    }
    if (index < 0)
    {
      index = NodeManager.total_nodes_chord - 1;
    }
    while (my_node_manager.rmi_NodeFind(index).endsWith("V"))
    {
      index--;
      if (index == NodeManager.total_nodes_chord)
      {
        index = 0;
      }
      if (index < 0)
      {
        index = NodeManager.total_nodes_chord - 1;
      }
    }
    Logger.out(" the predecessor is node " + index);
    //Logger.writeContent(NodeManager.LOGGER_FILE_NAME, " the predecessor is node " + index);
    return index;
  }
  
 
}
